/**
 * JAVACC DEMO 1.0
 */
package com.apache.rpc.client;

import com.apache.api.api.ApiService;
import com.apache.api.api.SystemService.ParamType;
import com.apache.api.entity.BaseEntity;
import com.apache.api.vo.ParamsVo;
import com.apache.api.vo.ResultEntity;
import com.apache.cache.service.impl.LoadCacheFactory;
import com.apache.cache.util.Validator;
import com.apache.database.constant.SpringContextLoader;
import com.apache.exception.BusinessException;
import com.apache.exception.ServiceException;
import com.apache.rpc.ProtocolService;
import com.apache.rpc.common.JsonEntityParese;
import com.apache.rpc.common.RpcUtil;
import com.apache.rpc.entity.InterfaceRegister;
import com.apache.rpc.entity.Protocol;
import com.apache.tools.OsUtils;
import com.apache.tools.StrUtil;
import org.apache.log4j.Logger;

import java.util.*;

/**
 * description:  通信适配器
 *
 * @author Hou Dayu 创建时间：2016-10-28
 */
public class ProtocolAdapter implements ProtocolService {

    private static ProtocolAdapter adapter;

    private Logger log = Logger.getLogger(ProtocolAdapter.class);

    private Map<String, AbstractProtocolService> services = new HashMap<String, AbstractProtocolService>();

    private ProtocolAdapter() {
        setServices(null);
    }

    public static synchronized ProtocolAdapter getAdapter() {
        if (null == adapter) {
            adapter = new ProtocolAdapter();
        }
        return adapter;
    }

    public Object doService(Protocol protocol) {
        ResultEntity result = null;
        InterfaceRegister register = null;
        String protocolBeanId = "";
        try {
            register = getServiceKey(protocol);
            if (Validator.isEmpty(register) || "internal"
                    .equals(register.getCallType())) {//直接执行本地服务
                ParamsVo<BaseEntity> vo = new ParamsVo<BaseEntity>();
                vo.setParams(protocol.getParams());
                vo.setMethodKey(protocol.getReqFlag());
                protocolBeanId = protocol.getReqFlag();
                result = doService(protocol.getBeanId(), vo);
            } else {
                protocolBeanId = register.getBeanId();
                log.info("beanId[" + protocolBeanId + "],address[" + register.getAddress()
                        + "],port[" + register.getPort() + "],version["+register.getReserve2()+"]");
                AbstractProtocolService ps = services.get(register.getCallType());
                ps.setRegister(register.getAddress(), register.getPort(), register.getAddress());
                result = ps.doService(register.getBeanId(), register.getMethodName(),
                        protocol.getParams());
            }
        } catch (Exception e) {
            log.info("接口[" + protocol.getBeanId() + "]执行出错：" + e + ";执行参数：" + protocol.getParams());
            throw new BusinessException("接口[" + protocol.getBeanId() + "]执行出错", e);
        } finally {
            if (Validator.isEmpty(register) || "internal".equals(register.getCallType())) {
                protocol.setParams("rpcExeTimeAddr", OsUtils.getLocalIp());
                protocol.setParams("rpcExeTimePort",
                        RpcUtil.getInstance().getValueByKey("rpc_service_port"));
            } else {
                protocol.setParams("rpcExeTimeAddr", register.getAddress());
                protocol.setParams("rpcExeTimePort", register.getPort());
            }
            if (result != null) {
                log.info(
                        "beanId[" + protocolBeanId + "],result[" + result.getResult() + "],message["
                                + result.getMessage() + "]");
            }
        }
        return result;
    }

    public String doServiceStr(Protocol protocol) {
        InterfaceRegister register = null;
        String result = null;
        try {
            register = getServiceKey(protocol);
            if (Validator.isEmpty(register) || "internal"
                    .equals(register.getCallType())) {//直接执行本地服务
                ParamsVo<BaseEntity> vo = new ParamsVo<BaseEntity>();
                vo.setParams(protocol.getParams());
                vo.setMethodKey(protocol.getReqFlag());
                ResultEntity entity = doService(protocol.getBeanId(), vo);
                result = JsonEntityParese.instance().toXml(entity);
            } else {
                AbstractProtocolService ps = services.get(register.getCallType());
                ps.setRegister(register.getAddress(), register.getPort(), register.getAddress());
                result = ps.doServiceStr(register.getBeanId(), register.getMethodName(),
                        protocol.getParams());
            }
        } catch (Exception e) {
            log.info("接口[" + protocol.getBeanId() + "]执行出错：" + e + ";执行参数：" + protocol.getParams());
            throw new BusinessException("接口[" + protocol.getBeanId() + "]执行出错", e);
        } finally {
            if (Validator.isEmpty(register) || "internal".equals(register.getCallType())) {
                protocol.setParams("rpcExeTimeAddr", OsUtils.getLocalIp());
                protocol.setParams("rpcExeTimePort",
                        RpcUtil.getInstance().getValueByKey("rpc_service_port"));
            } else {
                protocol.setParams("rpcExeTimeAddr", register.getAddress());
                protocol.setParams("rpcExeTimePort", register.getPort());
            }
        }
        return result;
    }

    public ResultEntity doService(String beanId, ParamsVo vo) {
        ResultEntity result = null;
        try {
            ApiService service = (ApiService) SpringContextLoader.getBean(beanId);
            if (null == service) {
                result = new ResultEntity();
                result.setMessage("接口[" + beanId + "]不存在");
                result.setResult("false");
                return result;
            }
            result = service.doService(vo);
        } catch (Exception e) {
            log.error(e);
            throw new ServiceException("接口[" + beanId + "->" + vo.getMethodKey() + "]执行出错", e);
        }
        return result;
    }

    private InterfaceRegister getServiceKey(Protocol protocol) {
        Map<String, InterfaceRegister> params = new HashMap<String, InterfaceRegister>(1);
        Map<String, List<InterfaceRegister>> maps = selectInterfaceRegisterCache(
                protocol.getBeanId(), "1", protocol.getReqFlag(), protocol.getProtocolType(),
                params);
        if (Validator.isEmpty(maps)) {
            return null;
        }
        InterfaceRegister register = null;
        if (null == params.get("default_" + protocol.getProtocolType())) {
            List<InterfaceRegister> list = maps.get(protocol.getProtocolType());
            if (!Validator.isEmpty(list)) {//指定的方式
                register = getInterfaceRegister(list, params.get(protocol.getProtocolType()));
            }
            if (Validator.isEmpty(register) && !"socket".equals(protocol.getProtocolType())
                    && !Validator.isEmpty(maps.get("socket"))) {//socket请求方式
                register = getInterfaceRegister(maps.get("socket"), params.get("default_socket"));
            }
            if (Validator.isEmpty(register) && !"http".equals(protocol.getProtocolType())
                    && !Validator.isEmpty(maps.get("http"))) {//http请求方式
                register = getInterfaceRegister(maps.get("http"), params.get("default_http"));
            }
            if (Validator.isEmpty(register) && !"https".equals(protocol.getProtocolType())
                    && !Validator.isEmpty(maps.get("https"))) {//https请求方式
                register = getInterfaceRegister(maps.get("https"), params.get("default_https"));
            }
        } else {
            register = params.get("default_" + protocol.getProtocolType());
        }
        if (Validator.isEmpty(register)) {
            return null;
        }
        register.setMethodName(protocol.getReqFlag());
        return register;
    }

    private InterfaceRegister getInterfaceRegister(List<InterfaceRegister> list,
            InterfaceRegister defaultRegister) {
        InterfaceRegister register = null;
        if (!Validator.isEmpty(list)) {//指定的方式
            if (list.size() > 1) {
                Random random = new Random();
                int index = random.nextInt(list.size() - 1);
                register = list.get(index);
            } else {
                if (Validator.isEmpty(defaultRegister)) {
                    register = list.get(0);
                } else {
                    register = defaultRegister;
                }
            }
        }
        return register;
    }

    private ParamType getParamType(String type) {
        if ("json".equalsIgnoreCase(type)) {
            return ParamType.JSON;
        } else {
            return ParamType.XML;
        }
    }

    /**
     * description:  按BeanId获取接口注册可用的信息
     *
     * @param beanId
     * @param flag
     * @param methodName
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Map<String, List<InterfaceRegister>> selectInterfaceRegisterCache(String beanId,
            String flag, String methodName, String type, Map<String, InterfaceRegister> params) {
        String cacheKey = "interfaceRegister_" + beanId;
        List<InterfaceRegister> list = (List<InterfaceRegister>) LoadCacheFactory.getInstance()
                .getCacheManager("").getCacheCloneByKey(cacheKey);
        if (Validator.isEmpty(list))
            return null;
        Map<String, List<InterfaceRegister>> resMap = new HashMap<String, List<InterfaceRegister>>();
        for (Object obj : list) {
            InterfaceRegister register = (InterfaceRegister) obj;
            List<InterfaceRegister> result = resMap.get(register.getCallType());
            if (Validator.isEmpty(result)) {
                result = new ArrayList<InterfaceRegister>();
            }
            if (Validator.isNotNull(register.getReserve2())) {
                if (RpcUtil.getVersion().equalsIgnoreCase(register.getReserve2())) {//验证版本号
                    params.put("default_" + register.getCallType(), register);
                }else{
//                    log.error("未找到注册的["+beanId+"]version["+RpcUtil.getVersion()+"]接口！！");
                }
            } else {
                result.add(register);
                resMap.put(register.getCallType(), result);
                if (type.equals(register.getCallType())) {
                    params.put(type, register);
                }
            }
        }
        return resMap;
    }

    public boolean isExites(String beanId) {
        if (StrUtil.isNull(beanId))
            return false;
        String cacheKey = "interfaceRegister_" + beanId;
        return LoadCacheFactory.getInstance().getCacheManager("").checkCacheObject(cacheKey);
    }

    public void setServices(Map<String, AbstractProtocolService> services) {
        //this.services = services;
        this.services.put("http", new HttpProtocolService());
        this.services.put("socket", new SocketProtocolService());
        this.services.put("https", new HttpsProtocolService());
    }

    public void setProtocolService(String key, AbstractProtocolService protocolService) {
        this.services.put(key, protocolService);
    }
}
